package com.wwmz.mebatis.v2.session;

import com.wwmz.mebatis.v2.TestMyBatis;
import com.wwmz.mebatis.v2.binding.MapperRegistry;
import com.wwmz.mebatis.v2.executor.CachingExecutor;
import com.wwmz.mebatis.v2.executor.Executor;
import com.wwmz.mebatis.v2.executor.SimpleExecutor;
import com.wwmz.mebatis.v2.plugin.Interceptor;
import com.wwmz.mebatis.v2.plugin.InterceptorChain;

import java.io.File;
import java.util.*;

/**
 * Created by wumz on 2020/10/17 10:55
 */
public class Configuration {

    // SQL映射关系配置，使用注解时不用重复配置
    public static final ResourceBundle sqlMappings;
    // 全局配置
    public static final ResourceBundle properties;
    //维护接口与工厂类关系
    public static final MapperRegistry mapperRegistry = new MapperRegistry();
    // 维护接口方法与SQL关系
    public static final Map<String, String> mappedStatements = new HashMap<>();

    // 类所有文件
    private List<String> classPaths = new ArrayList<>();
    // 所有Mapper接口
    private List<Class<?>> mapperList = new ArrayList<>();
    // 插件
    private InterceptorChain interceptorChain = new InterceptorChain();

    static {
        sqlMappings = ResourceBundle.getBundle("v2sql");
        properties = ResourceBundle.getBundle("mybatis");
    }


    public Configuration(){
        // Note：在properties和注解中重复配置SQL会覆盖
        // 1.解析sql.properties
        for (String key: sqlMappings.keySet()){
            Class<?> mapper = null;
            Class<?> pojo = null;
            // properties中的value用--隔开，第一个是SQL语句
            String statement = sqlMappings.getString(key).split("--")[0];
            // properties中的value用--隔开，第二个是需要转换的POJO类型
            String pojoStr = sqlMappings.getString(key).split("--")[1];
            try {
                // properties中的key是接口类型+方法
                // 从接口类型+方法中截取接口类型
                mapper = Class.forName(key.substring(0, key.lastIndexOf(".")));
                pojo = Class.forName(pojoStr);
            }catch (Exception e){
                e.printStackTrace();
            }

            // 保存接口与返回的实体类关系
            mapperRegistry.addMapper(mapper,pojo);
            mappedStatements.put(key, statement); // 接口方法与SQL关系
        }

        // 2.解析接口上的注解（会覆盖XML中的接口与实体类的关系）
        String mapperPath = properties.getString("mapper.path");
        scanPackage(mapperPath);

        // 3.解析插件，可配置多个插件
        String pluginPathValue = properties.getString("plugin.path");
        String[] pluginPaths = pluginPathValue.split(",");
        if (pluginPaths != null) {
            // 将插件添加到interceptorChain中
            for (String plugin : pluginPaths) {
                Interceptor interceptor = null;
                try {
                    interceptor = (Interceptor) Class.forName(plugin).newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                interceptorChain.addInterceptor(interceptor);
            }
        }
    }

    /**
     * 根据全局配置文件的Mapper接口路径，扫描所有接口
     * @param mapperPath
     */
    private void scanPackage(String mapperPath) {
        String classPath = TestMyBatis.class.getResource("/").getPath();
        mapperPath = mapperPath.replace(".", File.separator);
        String mainPath = classPath + mapperPath;
        doPath(new File(mainPath));
        for (String className : classPaths) {
            className = className.replace(classPath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if(clazz.isInterface()){
                mapperList.add(clazz);
            }
        }
    }

    /**
     * 获取文件或文件夹下所有的类.class
     * @param file
     */
    private void doPath(File file) {
        // 文件夹，遍历
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f1 : files) {
                doPath(f1);
            }
        } else {
            // 文件，直接添加
            if (file.getName().endsWith(".class")) {
                classPaths.add(file.getPath());
            }
        }
    }

    /**
     * 创建执行器，当开启缓存时使用缓存装饰
     * @return
     */
    public Executor newExecutor() {
        Executor executor = null;
        if (properties.getString("cache.enabled").equals("true")){
            executor = new CachingExecutor(new SimpleExecutor());
        } else {
            executor = new SimpleExecutor();
        }
        //目前只拦截了Executor，所有的插件都对Executor进行代理，没有对拦截类和方法签名进行判断
        if (interceptorChain.hasPlugin()) {
            return (Executor)interceptorChain.pluginAll(executor);
        }
        return executor;
    }

    /**
     * 根据statement ID获取SQL
     * @param clazz
     * @param sqlSession
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> clazz, DefaultSqlSession sqlSession) {
        return mapperRegistry.getMapper(clazz, sqlSession);
    }

    public boolean hasStatement(String statementId) {
        return mappedStatements.containsKey(statementId);
    }

    public String getMappedStatement(String statementId) {
        return mappedStatements.get(statementId);
    }
}
